package com.techblor.redis.library.message.proxy;

import com.techblor.redis.library.message.listener.RedisMessageListenerInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.Topic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * redis消息监听器动态代理处理器
 */
@Slf4j
public class RedisMessageListenerInvocationHandler implements InvocationHandler {

    private RedisMessageListenerInfo redisMessageListenerInfo;

    public RedisMessageListenerInvocationHandler(RedisMessageListenerInfo redisMessageListenerInfo) {
        this.redisMessageListenerInfo = redisMessageListenerInfo;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (!method.getName().equals("onMessage")) {
            // 由于 RedisMessageListenerContainer 在存MessageListener的时候直接用对象, 容器是HashMap所以会调用hashCode
            if (method.getName().equals("hashCode")) {
                return hashCode();
            } else {
                return 1;
            }
        }

        Method targetMethod = redisMessageListenerInfo.getMethod();
        List<Class<?>> parameterTypes = Arrays.asList(targetMethod.getParameterTypes());
        Object[] params = new Object[parameterTypes.size()];
        for (int i = 0; i < parameterTypes.size(); i++) {
            Class<?> aClass = parameterTypes.get(i);
            params[i] = getMethodParam(aClass, args);
        }

        try {
            return targetMethod.invoke(redisMessageListenerInfo.getObject(), params);
        } catch (Exception e) {
            log.error("监听频道: {}, 处理异常, 参数: {}", redisMessageListenerInfo.getTopics(), params);
            throw e;
        }

    }

    public Object getMethodParam(Class<?> paramType, Object[] args) {
        if (paramType.isAssignableFrom(String.class)) {
            return args[0].toString();
        } else if (paramType.isAssignableFrom(Message.class)) {
            return args[0];
        } else if (paramType.isAssignableFrom(byte[].class)) {
            return args[1];
        } else if (paramType.isAssignableFrom(Topic.class)) {
            return new ChannelTopic(new String((byte[]) args[1]));
        } else {
            return null;
        }
    }

}
